#include "easyShell.h"

#define ES_VERSION_MAIN                '0'
#define ES_VERSION_SUB                 '2'

// 自定义命令列表
static shellCmdTypedef shellCmdList[ES_CMD_MAX] = {{0}};
static uint8_t shellCmdCount = 0;

// 输入命令缓冲
static uint8_t cmdBuf[ES_CMD_LENGTH_MAX + 1] = {0};
static uint16_t cmdLen = 0;
static uint16_t cmdCurPos = 0;

// 按键识别缓存
static uint8_t kbBuf[4] = {0};
static uint8_t kbCount = 0;

// TAB功能变量
#if ES_TAB_ENABLE == 1
static uint8_t tabCount = 0;
#endif

esSysInfo_t esSysInfo={0};

bool easyShellAddCmd(shellCmdTypedef info)
{
    if (shellCmdCount < ES_CMD_MAX)
    {
        shellCmdList[shellCmdCount] = info;
        shellCmdCount++;
        return true;
    }
    return false;
}

void easyShellShowLogo(void)
{
    const uint8_t buf[] = "\r\n\r\n\
                       ____  _          _ _ \r\n\
   ___  __ _ ___ _   _/ ___|| |__   ___| | |\r\n\
  / _ \\/ _` / __| | | \\___ \\| '_ \\ / _ \\ | |\r\n\
 |  __/ (_| \\__ \\ |_| |___) | | | |  __/ | |\r\n\
  \\___|\\__,_|___/\\__, |____/|_| |_|\\___|_|_|\r\n\
                 |___/                      \r\n\r\n\
 https://gitee.com/gzbkey/easyShell.git\r\n";

    esSysInfo.sendFunc((uint8_t*)buf, sizeof(buf) - 1);
}

void easyShellShowCursor(void)
{
    esSysInfo.sendFunc((uint8_t*)">>", 2);
}

ES_WEAK int easyShellBootCmd(int argc, char** argv)
{
    //add code. jump app
    return 0;
}

void easyShellDefaultCmd(void)
{
    shellCmdTypedef cmdInfo;
    cmdInfo.pName="boot",
#if ES_CMD_BRIEF_ENABLE == 1
    cmdInfo.pBrief="run app",
#endif
    cmdInfo.pFunc=easyShellBootCmd,
    easyShellAddCmd(cmdInfo);
}

void easyShellInit(esSendFunc_t pUartSend)
{
    const uint8_t infoBuf[] = {'\n', '\r', ' ', 'E', 'a', 's', 'y', ' ', 'S', 'h', 'e', 'l', 'l', '\n', '\r', '\n', '\r',
                            ' ', 'V', 'e', 'r', 's', 'i', 'o', 'n', ':', ES_VERSION_MAIN, '.', ES_VERSION_SUB, '\n', '\r', '\n', '\r'};

    esSysInfo.sendFunc = pUartSend;
#if ES_LOGO_ENABLE == 1
    easyShellShowLogo();
#endif
    esSysInfo.sendFunc((uint8_t*)infoBuf, sizeof(infoBuf));
    easyShellShowCursor();
                            
    easyShellDefaultCmd();
}

void easyShellMultipleCmdReset(void)
{
    memset(kbBuf, 0, sizeof(kbBuf));
    kbCount = 0;
}

void easyShellCmdReset(void)
{
    easyShellMultipleCmdReset();
    cmdLen = 0;
    cmdCurPos = 0;
    memset(cmdBuf, 0, sizeof(cmdBuf));
}

void easyShellLoop(void)
{
    if (esSysInfo.loopFunc != NULL)
    {
        esSysInfo.loopFunc(0, NULL);
    }

    if(esSysInfo.isExit)
    {
        esSysInfo.tickFunc=NULL;
        esSysInfo.recvFunc=NULL;
        esSysInfo.loopFunc=NULL;
        esSysInfo.isExit=false;
        esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
        easyShellShowCursor();
        easyShellCmdReset();
    }
}

void easyShellTick(uint8_t cycleMs)
{
    // loop使用计时
    if(esSysInfo.tickFunc!=NULL)
    {
        esSysInfo.tickFunc(cycleMs);
    }
}

uint8_t easyShellCmdProcess(uint8_t *str, uint8_t **ret)
{
    uint8_t i;
    uint8_t *addr = NULL;
    uint8_t retCount = 0;

    addr = str;
    ret[0] = addr;
    retCount++;

    for (i = 0; i < ES_CMD_PARA_MAX; i++)
    {
        addr = (uint8_t *)strstr((const char *)addr, " ");
        if (addr != 0)
        {
            *addr = 0;
            addr++;
            ret[i + 1] = addr;
            retCount++;
        }
        else
        {
            break;
        }
    }
    return retCount;
}

void easyShellRunCmd(uint8_t *dat, uint16_t len)
{
    int i = 0;
    uint8_t *argv[ES_CMD_PARA_MAX + 1] = {0};
    uint8_t count;
    bool isRunLoop=false;

    count = easyShellCmdProcess(dat, argv);
    for (i = 0; i < shellCmdCount; i++)
    {
        if (strcmp(shellCmdList[i].pName, (const char *)argv[0]) == 0)
        {
            esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
            if(shellCmdList[i].pFunc(count, (char **)argv)!=(-1))
            {
                isRunLoop=true;
            }
            break;
        }
#if ES_CMD_BRIEF_ENABLE == 1
        if ((strcmp("man", (const char *)argv[0]) == 0) && (strcmp(shellCmdList[i].pName, (const char *)argv[1]) == 0))
        {
            esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
            esSysInfo.sendFunc((uint8_t*)shellCmdList[i].pBrief, strlen(shellCmdList[i].pBrief));
            break;
        }
#endif
    }

    if(!isRunLoop)
    {
        esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
        if ((i == shellCmdCount) && (len > 0)) // 无匹配
        {
            esSysInfo.sendFunc((uint8_t*)"Command not found\r\n", 19);
        }
        easyShellShowCursor();
    }
    easyShellCmdReset();
}

#if ES_TAB_ENABLE == 1

uint8_t easyShellListMatchCmd(uint8_t *nameStr, uint8_t *outNumList)
{
    uint8_t i;
    uint8_t *addr = NULL;
    uint8_t cmdMatchCount = 0;

    for (i = 0; i < shellCmdCount; i++)
    {
        addr = (uint8_t *)strstr(shellCmdList[i].pName, (const char *)nameStr);
        if (addr != 0)
        {
            outNumList[cmdMatchCount] = i;
            cmdMatchCount++;
        }
    }
    return cmdMatchCount;
}

// 自动换行
void easyShellListCmdAutoPrint(uint8_t num, uint8_t *dat, uint16_t length)
{
    static uint16_t count;
    if (num == 0)
    {
        count = 0;
    }

    if (length <= 80)
    {
        if (count + length < 80)
        {
            esSysInfo.sendFunc(dat, length);
        }
        else
        {
            count = 0;
            esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
            esSysInfo.sendFunc(dat, length);
        }
        count = count + length + 1;
        esSysInfo.sendFunc((uint8_t*)" ", 1);
    }
}

void easyShellListCmd(void)
{
    uint8_t *addr = NULL, *tempAddr = NULL;
    uint8_t cmdMatchNum[ES_CMD_MAX] = {0};
    uint8_t cmdMatchCount = 0;
    int16_t i;
    if (cmdLen == cmdCurPos)
    {
        // 反向查找空格
        for (i = cmdLen; i > 0; i--)
        {
            if (cmdBuf[i - 1] == ' ')
            {
                addr = &cmdBuf[i - 1];
                addr++;
            }
        }
        if (addr == NULL)
        {
            addr = cmdBuf;
        }
        // 匹配命令
        if (cmdLen != 0)
        {
            cmdMatchCount = easyShellListMatchCmd(addr, cmdMatchNum);
        }

        switch (cmdMatchCount)
        {
        case 0:
        {
            if (cmdBuf[0] == 0)
            {
                esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
                for (i = 0; i < shellCmdCount; i++)
                {
                    // 列出
                    easyShellListCmdAutoPrint(i,(uint8_t*) shellCmdList[i].pName, strlen(shellCmdList[i].pName));
                }
                esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
                easyShellShowCursor();
                esSysInfo.sendFunc(cmdBuf, cmdLen);
            }
            break;
        }
        case 1:
        {
            tempAddr = (uint8_t*)shellCmdList[cmdMatchNum[0]].pName;
            while (*tempAddr != 0)
            {
                if (*tempAddr != *addr)
                {
                    cmdBuf[cmdLen] = *tempAddr;
                    cmdLen++;
                    cmdCurPos++;
                    esSysInfo.sendFunc(tempAddr, 1);
                }
                tempAddr++;
                addr++;
            }
            break;
        }
        default:
        {
            if (tabCount == 0)
            {
                tabCount++;
                uint8_t newStrLen = 0;
                uint8_t strPos = 0;
                newStrLen = strlen((const char *)addr);
                strPos = newStrLen;
                // 判断下一次字节是否相同
                tempAddr = (uint8_t*)shellCmdList[cmdMatchNum[0]].pName;
                while (tempAddr[strPos] != 0)
                {
                    for (i = 1; i < cmdMatchCount; i++)
                    {
                        if (tempAddr[strPos] != shellCmdList[cmdMatchNum[i]].pName[strPos])
                        {
                            break;
                        }
                    }
                    if (i == cmdMatchCount)
                    {
                        cmdBuf[strPos] = tempAddr[strPos];
                        esSysInfo.sendFunc(&cmdBuf[strPos], 1);
                        cmdLen++;
                        cmdCurPos++;
                    }
                    strPos++;
                }
            }
            else
            {
                tabCount = 0;
                esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
                for (i = 0; i < cmdMatchCount; i++)
                {
                    // 列出
                    easyShellListCmdAutoPrint(i, (uint8_t*)shellCmdList[cmdMatchNum[i]].pName, strlen(shellCmdList[cmdMatchNum[i]].pName));
                }
                esSysInfo.sendFunc((uint8_t*)"\r\n", 2);
                easyShellShowCursor();
                esSysInfo.sendFunc(cmdBuf, cmdLen);
            }
            break;
        }
        }
    }
}

#endif

void easyShellReceiveData(uint8_t dat)
{
    uint16_t i;

    if (esSysInfo.sendFunc == NULL)
    {
        return;
    }
    if (esSysInfo.recvFunc != NULL)
    {
        esSysInfo.recvFunc(dat);
        return;
    }

    kbBuf[kbCount] = dat;

    switch (kbBuf[0])
    {
    case 0x03: // ETX (end of text)
    {
        esSysInfo.isExit=true;
        break;
    }
    // rn  r  n
    case '\n': // 换行
    case '\r': // 回车
    {
        easyShellRunCmd(cmdBuf, cmdLen);
        easyShellCmdReset();
        break;
    }
    case '\b': // Backspace
    {
        if (cmdLen > 0)
        {
            tabCount = 0;
            if (cmdCurPos == cmdLen)
            {
                esSysInfo.sendFunc((uint8_t*)"\b \b", 3);
                cmdBuf[cmdLen - 1] = 0;
                cmdLen--;
                cmdCurPos--;
            }
            else // 中间删除
            {
                for (i = cmdCurPos; i < cmdLen; i++)
                {
                    cmdBuf[i - 1] = cmdBuf[i];
                }
                cmdLen--;
                cmdCurPos--;

                cmdBuf[cmdLen] = ' ';

                esSysInfo.sendFunc((uint8_t*)"\b", 1);
                esSysInfo.sendFunc(&cmdBuf[cmdCurPos], cmdLen - cmdCurPos + 1);
                for (i = 0; i < cmdLen - cmdCurPos + 1; i++)
                {
                    esSysInfo.sendFunc((uint8_t*)"\b", 1);
                }
            }
        }
        break;
    }
    // 左箭头  1B 5B 44
    // 右箭头  1B 5B 43
    // Delete  1B 5B 33 7E
    case 0x1B:
    {
        kbCount++;
#if ES_CURSOR_MOVE_ENABLE == 1
        if (kbCount == 3)
        {
            // 左箭头  1B 5B 44
            if ((kbBuf[0] == 0x1B) && (kbBuf[1] == 0x5B) && (kbBuf[2] == 0x44))
            {
                if (cmdCurPos > 0)
                {
                    esSysInfo.sendFunc((uint8_t*)"\b", 1);
                    cmdCurPos--;
                    easyShellMultipleCmdReset();
                }
            }
            // 右箭头  1B 5B 43
            if ((kbBuf[0] == 0x1B) && (kbBuf[1] == 0x5B) && (kbBuf[2] == 0x43))
            {
                if (cmdCurPos < cmdLen)
                {
                    esSysInfo.sendFunc(&cmdBuf[cmdCurPos], 1);
                    cmdCurPos++;
                    easyShellMultipleCmdReset();
                }
            }
        }
#endif
        if (kbCount == 4)
        {
            // Delete  1B 5B 33 7E
            if ((kbBuf[0] == 0x1B) && (kbBuf[1] == 0x5B) && (kbBuf[2] == 0x33) && (kbBuf[3] == 0x7E))
            {
                if (cmdCurPos < cmdLen)
                {
                    for (i = cmdCurPos; i < (cmdLen - 1); i++)
                    {
                        cmdBuf[i] = cmdBuf[i + 1];
                    }
                    cmdBuf[cmdLen - 1] = ' ';
                    esSysInfo.sendFunc(&cmdBuf[cmdCurPos], cmdLen - cmdCurPos + 1);
                    for (i = cmdCurPos; i < (cmdLen); i++)
                    {
                        esSysInfo.sendFunc((uint8_t*)"\b", 1);
                    }
                    cmdLen--;
                    easyShellMultipleCmdReset();
                }
            }
        }
        break;
    }
    case '\t': // tab
    {
#if ES_TAB_ENABLE == 1
        easyShellListCmd();
#endif
        break;
    }
    default:
    {
        if (cmdLen < ES_CMD_LENGTH_MAX)
        {
            tabCount = 0;
            if (cmdCurPos == cmdLen)
            {
                cmdBuf[cmdLen++] = dat;
                cmdCurPos++;
                esSysInfo.sendFunc(&dat, 1);
            }
            else // 插入
            {
                for (i = cmdLen; i > cmdCurPos; i--)
                {
                    cmdBuf[i] = cmdBuf[i - 1];
                }
                cmdLen++;
                cmdBuf[cmdCurPos] = dat;
                esSysInfo.sendFunc(&cmdBuf[cmdCurPos], cmdLen - cmdCurPos);
                cmdCurPos++;
                for (i = 0; i < cmdLen - cmdCurPos; i++)
                {
                    esSysInfo.sendFunc((uint8_t*)"\b", 1);
                }
            }
        }
    }
    }
}
